﻿using System;
using Driven.ProcessScheduler.Interfaces;

namespace Driven.ProcessScheduler
{
    public interface ISchedule
    {
        string Name { get; set; }

        /// <summary>
        /// The component for which this schedule has been created
        /// </summary>
        IScheduledComponent Component { get; set; }

        /// <summary>
        /// The time of day to run the schedule
        /// </summary>
        TimeSpan TimeSchedule { get; set; }

        /// <summary>
        /// The time, in minutes, to repeat the execution of the process
        /// </summary>
        int RepetitionSchedule { get; set; }

        /// <summary>
        /// The time of day at which this component was last executed (Will be DateTime.MaxValue if it has never run)
        /// </summary>
        DateTime LastRan { get; }

        bool IsDue { get; }
        DateTime NextRunDateTime { get; }
        string[] Execute();
        string ToString();
    }

    public class Schedule : ISchedule
    {
        private TimeSpan _timeSchedule;
        private int _repetitionSchedule;

        public string Name { get; set; }

        public IScheduledComponent Component { get; set; }

        public TimeSpan TimeSchedule
        {
            get { return _timeSchedule; }
            set
            {
                _timeSchedule = value;
                _repetitionSchedule = 0;
            }
        }

        public int RepetitionSchedule
        {
            get { return _repetitionSchedule; }
            set
            {
                _repetitionSchedule = value;
                _timeSchedule = TimeSpan.Zero;
            }
        }

        public DateTime LastRan { get; private set; }

        public bool IsDue { get { return DateTime.Now > NextRunDateTime; } }

        public Schedule(string name, IScheduledComponent component, TimeSpan timeSchedule, int repetitionSchedule)
        {
            if (timeSchedule.Ticks > 0 && repetitionSchedule > 0) throw new InvalidOperationException("Cannot create a schedule with both a Time and a Repetition schedule!");

            Name = name;
            Component = component;
            _timeSchedule= timeSchedule;
            _repetitionSchedule = repetitionSchedule;
            LastRan = DateTime.MaxValue;
        }

        public DateTime NextRunDateTime
        {
            get 
            {
                if (LastRan == DateTime.MaxValue) return DateTime.Now.AddSeconds(-1);

                var nextRun = DateTime.MaxValue;

                if (TimeSchedule.Ticks != 0)
                {
                    nextRun = LastRan.Day != DateTime.Now.Day ? 
                        DateTime.Today.Add(TimeSchedule) : 
                        DateTime.Today.Add(TimeSchedule).AddDays(1);
                }

                if (RepetitionSchedule != 0)
                {
                    nextRun = LastRan.AddMinutes(RepetitionSchedule);
                }

                return nextRun;
            }
        }

        public virtual string[] Execute()
        {
            var tmp = new string[0];

            if (IsDue)
            {
                tmp = Component.Execute();
                LastRan = DateTime.Now;
            }
            //else
            //    tmp = new string[] { this.Component.ToString() + ": Only scheduled to run at " + TimeSchedule.ToString() };

            return tmp;
        }

        public override string ToString()
        {
            return Name;
        }
    }
}